/*
 * Bunisoft the Open Source Communications Company
 * Copyright 2006, Bunisoft Inc.,
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */
package org.teremail.panto;

import java.io.Reader;
import java.io.StringReader;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * <p>An indvidiual header in a body or email header.  It assumes that a header
 * follows the format <code>name: type(/subype)?(; param-name=param-value)*</code><p>
 * 
 * <p>The type value will generally always be set, with the subtype set optionally
 * (probably only for Content-Type) and the option parameters.</p>
 * 
 * <p>It is sub-classes for specific header types.</p>
 * 
 * @author Michael Barker
 *
 */
public abstract class ContentHeader {

    private final String text;
    private final String type;
    private final String subType;
    private final Map<String, String> parameters;

    /**
     * Construct a content header
     * @param text The original text from the mail.
     * @param type The type.  I.e. the first part of the value.
     * @param subType The subtype, the second part of the value (null for all
     * except Content-Type.
     * @param parameters The additional parameters for the content type.
     */
    public ContentHeader(String text, String type, String subType,
            Map<String, String> parameters) {
        this.text = text;
        this.type = type;
        this.subType = subType;
        this.parameters = Collections.unmodifiableMap(parameters);
    }

    protected static String stripNewlines(String s) {
        return s.replaceAll("\r|\n", "");
    }
    
    protected static String toLowerCase(String s) {
        if (s != null) {
            return s.toLowerCase();
        }
        return s;
    }
    
    /**
     * Returns the option parameters.  Can be empty.
     * 
     * @return the parameters
     */
    public String getParameter(String key) {
        String value = parameters.get(key);
        if (value == null) {
            return getDefaultParameter(key);
        } else {
            return value;
        }
    }

    /**
     * Returns the subtype, can be null.
     * 
     * @return the subType
     */
    public String getSubType() {
        return subType;
    }

    /**
     * Returns the type, should always be set.
     * 
     * @return the type
     */
    public String getType() {
        return type;
    }
    
    public abstract String getName();
    public abstract String getDefaultParameter(String key);

    @Override
    public String toString() {
        if (text == null) {
            StringBuilder sb = new StringBuilder();
            sb.append(getName()).append(": ").append(getType());
            String subType = getSubType();
            if (subType != null) {
                sb.append("/").append(subType);
            }
            if (parameters.size() > 0) {
                for (Map.Entry<String,String> e : parameters.entrySet()) {
                    sb.append(";");
                    sb.append(" ").append(e.getKey()).append("=").append(e.getValue());
                }
            }
            return sb.toString();
        } else {
            return text;
        }
    }
    
    public static <T> T create(String line, Pattern pattern, 
            ContentHeaderFactory<T> factory) {
        
        T t = null;
        Matcher m = pattern.matcher(line);
        if (m.matches() && m.groupCount() == 1) {
            String value = m.group(1);
            Reader r = new StringReader(stripNewlines(value));
            ContentTypeParser parser = new ContentTypeParser(r);
            try {
                parser.parseAll();
            } catch (ParseException e) {
                System.err.printf("Line: %s", line);
                System.err.print(e.getMessage());
            }
            String type = toLowerCase(parser.getType());
            String subType = toLowerCase(parser.getSubType());
            Map<String,String> parameters = new HashMap<String, String>();
            List<String> names = parser.getParamNames();
            List<String> values = parser.getParamValues();
            for (int i = 0; i < names.size() && i < values.size(); i++) {
                String name = toLowerCase(names.get(i));
                parameters.put(name, values.get(i));
            }
            t = factory.create(line, type, subType, parameters);
        }
        
        return t;
    }
    
    protected interface ContentHeaderFactory<T> {
        T create(String text, String type, String subType, 
                Map<String,String> parameters);
    }

}